iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0

當開始有標籤專屬頁面後,這時候就會更近一步想要有一個標籤列表,讓我知道 Digital Garden 上有哪些標籤、各有多少文章。今天就來建立標籤清單的頁面吧!

首先,先建立 @/pages/tags/index.vue,並且輸入範例程式碼。今天的範例程式碼還沒精簡過,為了方便閱讀,就將 <template /><script /> 分開檢視吧。

<template>
  <div>
    <div
      class="flex flex-row divide-gray-200 mt-24 items-center justify-center space-x-6 divide-y-0"
    >
      <div class="space-x-2 pt-6 pb-8 space-y-5">
        <h1
          class="font-extrabold leading-9 tracking-tight text-gray-900 border-r-2 px-6 text-6xl leading-14"
        >
          Tags
        </h1>
      </div>
      <ul class="flex max-w-lg flex-wrap list-none">
        <li v-for="tag in articleTags" :key="tag" class="mt-2 mb-2 mr-5 no-underline">
          <PostTag :text="tag" />
          <NuxtLink
            :href="'/tags/' + kebabCase(tag)"
            class="-ml-2 text-sm font-semibold uppercase text-gray-600 no-underline"
            aria-label="
          posts
          tagged
          ${tag}`"
          >
            ({{ tagsCount[tag] }})
          </NuxtLink>
        </li>
      </ul>
    </div>
  </div>
</template>

在 Template 部分,透過 TailwindCSS 的 Flex Direction 將頁面做點變化,讓標題與標籤清單並列在同一個水平上,這樣在當前標籤還不多時,比較不會有單薄的感覺。

並遍歷 articleTags 這個變數,產生一系列的標籤項目,在標籤項目中我們複用了在 Article 中有用到的 PostTag 元件,並在旁邊新增其數量,數量則取自於 tagsCount 這個物件。

<script setup>
import { kebabCase, toLower } from 'lodash-es/string'

const tagsCount = {}

// [TODO]: extract to composable
const flatten = (tagsList, key) => {
  const _tags = tagsList
    .map((element) => {
      let _e = element

      const whenElementIsPost = typeof element === 'object'
      if (whenElementIsPost) {
        if (!element[key]) {
          element[key] = []
        }
        const tags = element[key]
        const flattened = flatten(tags)
        _e = flattened
      }

      const whenElementIsTag = typeof key === 'undefined'
      if (whenElementIsTag) {
        const tag = toLower(_e)
        tagsCount[tag] = tagsCount[tag] ? tagsCount[tag] + 1 : 1
        _e = tag
      }
      return _e
    })
    .flat(1)
  return _tags
}

const { data } = await useAsyncData('tags', () =>
  queryContent('/articles')
    .only(['tags'])
    .where({ tags: { $exists: true } })
    .where({ published_at: { $ne: null } })
    .find()
)

const flat = [...new Set(flatten(data.value, 'tags'))]
const articleTags = flat.filter((tag) => {
  return typeof tag === 'string' && tag.length > 0
})
</script>

在程式碼的部分,先透過查詢方法取得每一篇文章,並且透過 where() 限定擁有 tags 屬性的文章。另外比較有趣的部分是,透過 only() 來只取得其 tags 屬性,來節省空間使用。

接著利用上面宣告的 flatten() 函式,協助我們將這些文章的 tags 屬性扁平化,建立一個不重複的 tags 清單。並在之中協助計算 tags 在多少文章出現過,並將數量記錄在前面用到的 tagsCount 物件。

成果如圖:


上一篇
為標籤建立專屬頁面
下一篇
將常用的格式元件化
系列文
用 Nuxt Content 搭配 Obsidian 建立自己的 Digital Garden30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言